﻿/**
 * @file ut_rect.h
 * @author 贺亚猛
 * @brief 矩形实现类，封装矩形相关数据以及算法
 * @version 0.1
 * @date 2022-01-21
 * @copyright Copyright 2022 Langge Software Co., Ltd. All rights reserved.
 */

#ifndef UT_RECT_H
#define UT_RECT_H

#include "ut_symbols.h"
#include "ut_point.h"
#include "ut_math.h"
#include "ut_vector2.h"
#include <cmath>

BEGIN_SPACE_LANGE_VDR

/**
 * @brief 矩形实现类，封装矩形相关数据以及算法
 * @tparam T 数据类型
 */
template<typename T>
class UT_API UTRect
{
public:
    using Point=UT2DPoint<T>;
public:
    /**
     * @brief 构造函数
     */
    UTRect();
    /**
     * @brief 构造函数
     * @param minX - x下限
     * @param minY - y下限 
     * @param maxX - x上限
     * @param maxY - y上限
     */
    UTRect(const T minX,const T minY,
           const T maxX,const T maxY);
    /**
     * @brief 拷贝构造函数
     * @param rect  拷贝源
     */
    UTRect(const UTRect &rect);
    /**
     * @brief 析构函数
     */
    ~UTRect();
public:
    /**
     * @brief 将当前矩形框，设置为无效
     */
    void SetNan();
    /**
     * @brief 设置矩形最小x分量
     * @param x   x分量
     */
    void SetMinX(const T x);
    /**
     * @brief 获取矩形中最小x分量
     * @return 返回矩形中最小x分量
     */
    T GetMinX() const;
    /**
     * @brief 设置矩形中最小y分量
     * @param y y分量
     */
    void SetMinY(const T y);
    /**
     * @brief 获取矩形中最小y分量
     * @return 返回矩形中最小y分量
     */
    T GetMinY() const;
    /**
     * @brief 设置矩形中最大x分量
     * @param x        最大x分量
     */
    void SetMaxX(const T x);
    /**
     * @brief  获取矩形中最大x分量
     * @return 返回矩形中最大x分量
     */
    T GetMaxX() const;
    /**
     * @brief 设置矩形中最大y分量
     * @param y        最大y分量
     */
    void SetMaxY(const T y);
    /**
     * @brief 获取矩形中最大y分量
     * @return 返回矩形中最大y分量
     */
    T GetMaxY() const;
    /**
     * @brief 获取当前矩形宽度
     * @return 返回当前矩形宽度
     */
    T GetWidth() const;
    /**
     * @brief 获取当前矩形高度
     * @return 返回当前矩形高度
     */
    T GetHeight() const;
    /**
     * @brief 获取矩形中心点
     * @return 返回矩形中心点
     */
    Point GetCenter() const;
    /**
     * @brief 获取矩形原点
     * @return 返回矩形原点
     */
    Point GetOrigin() const;
    /**
     * @brief 检测当前矩形中数据是否为NAN
     * @return true       数据为NAN
     * @return false      数据不为NAN
     */
    bool IsNan() const;
    /**
     * @brief 检测与另一个矩形是否相交
     * @param other     另一个矩形
     * @param coef      相交判断系数，用来作为判断相交依据，默认为0
     * @return true     相交
     * @return false    不相交
     */
    bool Intersects(const UTRect& other,const T coef=static_cast<T>(0)) const;
    /**
     * @brief  检测当前矩形与另一个矩形是否为包含关系
     * @param  other    待检测矩形框
     * @return true     如果包含
     * @return false    如果不包含
     */
    bool Contains(const UTRect& other) const;
    /**
     * @brief  检测当前矩形是否与指定点位包含关系
     * @param  pt   指定点
     * @param  coef 包含判断系数，用来作为判断包含依据，默认为0
     * @return true   如果包含
     * @return false  如果不包含
     */
    bool Contains(const Point& pt,const T coef=static_cast<T>(0)) const;
    /**
     * @brief 规范化，使得矩形数据中最小x一定小于等于最大x
     *        Y坐标亦然
     */
    void Normalize();
    /**
     * @brief 将当前矩形与另一个矩形进行合并
     * @param other 另一个矩形
     */
    void Merge(const UTRect& other);
    /**
     * @brief 移动当前矩形位置
     * @param xoffset  x方向偏移量
     * @param yoffset  y方向偏移量
     */
    void Move(const T& xoffset,const T& yoffset);
    /**
     * @brief 拓展矩形，将矩形拓展到指定位置
     * @param x    x方向位置
     * @param y    y方向位置
     */
    void Expand(const T& x,const T& y);
public:
    /**
     * @brief 重载=运算符，用于实现矩形赋值
     * @param other 赋值源矩形
     * @return 返回自身引用
     */
    UTRect &operator=(const UTRect &other);
    /**
     * @brief 重载<运算符，用于比较矩形
     * @param other - 待比较矩形
     * @return true - 如果小于待比较矩形
     * @return false - 如果不小于待比较矩形
     */
    bool operator<(const UTRect &other) const;
    /**
     * @brief 重载==运算符，用于矩形相等判断
     * @param other - 待比较矩形
     * @return true - 如果两个矩形相等
     * @return false - 如果两个矩形不等
     */
    bool operator==(const UTRect &other) const;
    /**
     * @brief 重载!=运算符，用于矩形不等判断
     * @param other  - 待比较矩形
     * @return true  - 如果两个矩形不等
     * @return false - 如果两个矩形相等
     */
    bool operator!=(const UTRect &other) const;
    /**
     * @brief 重载&运算符，用于矩形与运算
     * @param other - 待运算矩形
     * @return 返回运算结果矩形
     */
    UTRect operator&(const UTRect &other) const;
    /**
     * @brief 重载|运算符，用于矩形或运算
     * @param other - 待运算矩形
     * @return 返回运算结果矩形
     */
    UTRect operator|(const UTRect &other) const;
    /**
     * @brief 重载*运算符，用于矩形与数乘算法,从而实现矩形缩放
     * @param scale 矩形缩放比例
     * @return 返回运算结果
     */
    UTRect operator*(const UTVector2<T>& scale) const;
public:
    /**
     * @brief 检测q点是否与p1、p2围成的矩形相交
     * @param p1   矩形点1
     * @param p2   矩形点2
     * @param q    指定点
     * @return true     如果相交
     * @return false    如果不相交
     */
    static bool Intersects(const Point& p1,const Point& p2,const Point& q);
    /**
     * @brief 检测由四个点围成的矩形是否相交
     * @param p1   矩形p的点1
     * @param p2   矩形p的点2
     * @param q1   矩形q的点1
     * @param q2   矩形q的点2
     * @return true - 如果相交
     * @return false - 如果不相交
     */
    static bool Intersects(const Point& p1, const Point& p2,
                           const Point& q1, const Point& q2);
private:
    T m_tMinX;
    T m_tMinY;
    T m_tMaxX;
    T m_tMaxY;
};

/**
 * @brief 构造函数
 * @tparam T 数据类型
 */
template<typename T>
UTRect<T>::UTRect()
    :m_tMinX(static_cast<T>(0))
    ,m_tMinY(static_cast<T>(0))
    ,m_tMaxX(static_cast<T>(0))
    ,m_tMaxY(static_cast<T>(0))
{
}

/**
 * @brief 构造函数特化版本
 */
template<>
inline UTRect<float>::UTRect()
    :m_tMinX(NAN)
    ,m_tMinY(NAN)
    ,m_tMaxX(NAN)
    ,m_tMaxY(NAN)
{
}

/**
 * @brief 构造函数特化版本
 */
template<>
inline UTRect<double>::UTRect()
    :m_tMinX(NAN)
    ,m_tMinY(NAN)
    ,m_tMaxX(NAN)
    ,m_tMaxY(NAN)
{
}

/**
 * @brief 构造函数特化版本
 */
template<>
inline UTRect<long double>::UTRect()
    :m_tMinX(NAN)
    ,m_tMinY(NAN)
    ,m_tMaxX(NAN)
    ,m_tMaxY(NAN)
{
}

/**
 * @brief 构造函数
 * @param minX - x下限
 * @param minY - y下限 
 * @param maxX - x上限
 * @param maxY - y上限
 */
template<typename T>
UTRect<T>::UTRect(const T minX,const T minY,const T maxX,const T maxY)
    :m_tMinX(minX)
    ,m_tMinY(minY)
    ,m_tMaxX(maxX)
    ,m_tMaxY(maxY)
{
    Normalize();
}

/**
 * @brief 拷贝构造函数
 * @param rect  拷贝源
 */
template<typename T>
UTRect<T>::UTRect(const UTRect &rect)
    :m_tMinX(rect.m_tMinX)
    ,m_tMinY(rect.m_tMinY)
    ,m_tMaxX(rect.m_tMaxX)
    ,m_tMaxY(rect.m_tMaxY)
{
    Normalize();
}

/**
 * @brief 析构函数
 */
template<typename T>
UTRect<T>::~UTRect()
{
}

/**
 * @brief 将当前矩形框，设置为无效
 */
template<typename T>
void UTRect<T>::SetNan()
{
    m_tMinX=static_cast<T>(0);
    m_tMinY=static_cast<T>(0);
    m_tMaxX=static_cast<T>(0);
    m_tMaxY=static_cast<T>(0);
}

/**
 * @brief 将当前矩形框，设置为无效
 */
template<>
inline void UTRect<float>::SetNan()
{
    m_tMinX=NAN;
    m_tMinY=NAN;
    m_tMaxX=NAN;
    m_tMaxY=NAN;
}

/**
 * @brief 将当前矩形框，设置为无效
 */
template<>
inline void UTRect<double>::SetNan()
{
    m_tMinX=NAN;
    m_tMinY=NAN;
    m_tMaxX=NAN;
    m_tMaxY=NAN;
}

/**
 * @brief 设置矩形最小x分量
 * @param x   x分量
 */
template<typename T>
void UTRect<T>::SetMinX(const T x)
{
    m_tMinX=x;
    Normalize();   
}

/**
 * @brief 获取矩形中最小x分量
 * @return 返回矩形中最小x分量
 */
template<typename T>
T UTRect<T>::GetMinX() const
{
    return m_tMinX;
}

/**
 * @brief 设置矩形中最小y分量
 * @param y y分量
 */
template<typename T>
void UTRect<T>::SetMinY(const T y)
{
    m_tMinY=y;
    Normalize();
}

/**
 * @brief 获取矩形中最小y分量
 * @return 返回矩形中最小y分量
 */
template<typename T>
T UTRect<T>::GetMinY() const
{
    return m_tMinY;
}

/**
 * @brief 设置矩形中最大x分量
 * @param x        最大x分量
 */
template<typename T>
void UTRect<T>::SetMaxX(const T x)
{
    m_tMaxX=x;
    Normalize();
}

/**
 * @brief  获取矩形中最大x分量
 * @return 返回矩形中最大x分量
 */
template<typename T>
T UTRect<T>::GetMaxX() const
{
    return m_tMaxX;
}

/**
 * @brief 设置矩形中最大y分量
 * @param y        最大y分量
 */
template<typename T>
void UTRect<T>::SetMaxY(const T y)
{
    m_tMaxY=y;
    Normalize();
}

/**
 * @brief 获取矩形中最大y分量
 * @return 返回矩形中最大y分量
 */
template<typename T>
T UTRect<T>::GetMaxY() const
{
    return m_tMaxY;
}

/**
 * @brief 获取当前矩形宽度
 * @return 返回当前矩形宽度
 */
template<typename T>
T UTRect<T>::GetWidth() const
{
    return m_tMaxX-m_tMinX;
}

/**
 * @brief 获取当前矩形高度
 * @return 返回当前矩形高度
 */
template<typename T>
T UTRect<T>::GetHeight() const
{
    return m_tMaxY-m_tMinY;
}

/**
 * @brief 获取矩形中心点
 * @return 返回矩形中心点
 */
template<typename T>
typename UTRect<T>::Point UTRect<T>::GetCenter() const
{
    return Point((m_tMinX+m_tMaxX)/static_cast<T>(2),
        (m_tMinY+m_tMaxY)/static_cast<T>(2));
}

/**
 * @brief 获取矩形原点
 * @return 返回矩形原点
 */
template<typename T>
typename UTRect<T>::Point UTRect<T>::GetOrigin() const
{
    return Point(m_tMinX,m_tMinY);
}

/**
 * @brief 检测当前矩形中数据是否为NAN
 * @return true       数据为NAN
 * @return false      数据不为NAN
 */
template<typename T>
bool UTRect<T>::IsNan() const
{
    return UTMath::IsNan(m_tMinX)||UTMath::IsNan(m_tMinY)||
           UTMath::IsNan(m_tMaxX)||UTMath::IsNan(m_tMaxY);
}

/**
 * @brief 检测与另一个矩形是否相交
 * @param other     另一个矩形
 * @param coef      相交判断系数，用来作为判断相交依据，默认为0
 * @return true     相交
 * @return false    不相交
 */
template<typename T>
bool UTRect<T>::Intersects(const UTRect& other,const T coef/*=static_cast<T>(0)*/) const
{
    if (IsNan()||other.IsNan())
    {
        return false;
    }
    else
    {
        return !(other.m_tMinX-m_tMaxX>=static_cast<T>(-coef)||
                 other.m_tMaxX-m_tMinX<=static_cast<T>(coef)||
                 other.m_tMinY-m_tMaxY>=static_cast<T>(-coef)||
                 other.m_tMaxY-m_tMinY<=static_cast<T>(coef));
    }
}

/**
 * @brief  检测当前矩形与另一个矩形是否为包含关系
 * @param  other    待检测矩形框
 * @return true     如果包含
 * @return false    如果不包含
 */
template<typename T>
bool UTRect<T>::Contains(const UTRect& other) const
{
    return !(m_tMinX>other.m_tMinX||m_tMaxX<other.m_tMaxX||
             m_tMinY>other.m_tMinY||m_tMaxY<other.m_tMaxY);
}

/**
 * @brief  检测当前矩形是否与指定点位包含关系
 * @param  pt   指定点
 * @param  coef 包含判断系数，用来作为判断包含依据，默认为0
 * @return true   如果包含
 * @return false  如果不包含
 */
template<typename T>
bool UTRect<T>::Contains(const Point& pt,const T coef/*=static_cast<T>(0)*/) const
{
    return !(m_tMinX-coef>pt.GetX()||m_tMaxX+coef<pt.GetX()||
             m_tMinY-coef>pt.GetY()||m_tMaxY+coef<pt.GetY());
}

/**
 * @brief 规范化，使得矩形数据中最小x一定小于等于最大x
 *        Y坐标亦然
 */
template<typename T>
void UTRect<T>::Normalize()
{
    if (m_tMinX>m_tMaxX)
    {
        T tmp=static_cast<T>(0);
        tmp=m_tMinX;
        m_tMinX=m_tMaxX;
        m_tMaxX=tmp;
    }
    if (m_tMinY>m_tMaxY)
    {
        T tmp=static_cast<T>(0);
        tmp=m_tMinY;
        m_tMinY=m_tMaxY;
        m_tMaxY=tmp;
    }
}

/**
 * @brief 将当前矩形与另一个矩形进行合并
 * @param other 另一个矩形
 */
template<typename T>
void UTRect<T>::Merge(const UTRect& other)
{    
    if(other.IsNan())
    {
        return;
    }
    if(IsNan())
    {
        *this=other;
    }
    else 
    {
        m_tMinX=UTMath::Min(m_tMinX,other.m_tMinX);
        m_tMaxX=UTMath::Max(m_tMaxX,other.m_tMaxX);
        m_tMinY=UTMath::Min(m_tMinY,other.m_tMinY);
        m_tMaxY=UTMath::Max(m_tMaxY,other.m_tMaxY);
    }
    Normalize();
}

/**
 * @brief 移动当前矩形位置
 * @param xoffset  x方向偏移量
 * @param yoffset  y方向偏移量
 */
template<typename T>
void UTRect<T>::Move(const T &xoffset,const T &yoffset)
{
    m_tMinX+=xoffset;
    m_tMinY+=yoffset;
    m_tMaxX+=xoffset;
    m_tMaxY+=yoffset;
    Normalize();
}

/**
 * @brief 拓展矩形，将矩形拓展到指定位置
 * @param x    x方向位置
 * @param y    y方向位置
 */
template<typename T>
void UTRect<T>::Expand(const T &x,const T &y)
{
    if(IsNan())
    {
        m_tMinX=m_tMaxX=x;
        m_tMinY=m_tMaxY=y;
        return;
    }

    if (m_tMinX>x)
    {
        m_tMinX=x;
    }
    else if(m_tMaxX<x)
    {
        m_tMaxX=x;
    }

    if(m_tMinY>y)
    {
        m_tMinY=y;
    }
    else if(m_tMaxY<y)
    {
        m_tMaxY=y;
    }

    Normalize();
}

/**
 * @brief 重载=运算符，用于实现矩形赋值
 * @param other 赋值源矩形
 * @return 返回自身引用
 */
template<typename T>
UTRect<T> &UTRect<T>::operator=(const UTRect &other)
{
    if (&other!=this)
    {
        m_tMinX=other.m_tMinX;
        m_tMinY=other.m_tMinY;
        m_tMaxX=other.m_tMaxX;
        m_tMaxY=other.m_tMaxY;
        Normalize();
    }
    return *this;
}

/**
 * @brief 重载<运算符，用于比较矩形
 * @param other - 待比较矩形
 * @return true - 如果小于待比较矩形
 * @return false - 如果不小于待比较矩形
 */
template<typename T>
bool UTRect<T>::operator<(const UTRect &other) const
{
    const Point center=GetCenter();
    const Point ocenter=other.GetCenter();
    if(0!=UTMath::Compare(center.GetX(),ocenter.GetX()))
    {
        return center.GetX()<ocenter.GetX();
    }
    if(0!=UTMath::Compare(center.GetY(),ocenter.GetY()))
    {
        return center.GetY()<ocenter.GetY();
    }

    const T width=GetWidth();
    const T owidth=other.GetWidth();
    if(0!=UTMath::Compare(width,owidth))
    {
        return width<owidth;
    }

    const T height=GetHeight();
    const T oheight=other.GetHeight();
    if(0!=UTMath::Compare(height,oheight))
    {
        return height<oheight;
    }

    return false;
}

/**
 * @brief 重载==运算符，用于矩形相等判断
 * @param other - 待比较矩形
 * @return true - 如果两个矩形相等
 * @return false - 如果两个矩形不等
 */
template<typename T>
bool UTRect<T>::operator==(const UTRect &other) const
{
    return 0==UTMath::Compare(m_tMinX,other.m_tMinX)&&
           0==UTMath::Compare(m_tMinY,other.m_tMinY)&&
           0==UTMath::Compare(m_tMaxX,other.m_tMaxX)&&
           0==UTMath::Compare(m_tMaxY,other.m_tMaxY);
}

/**
 * @brief 重载!=运算符，用于矩形不等判断
 * @param other  - 待比较矩形
 * @return true  - 如果两个矩形不等
 * @return false - 如果两个矩形相等
 */
template<typename T>
bool UTRect<T>::operator!=(const UTRect &other) const
{
    return !(*this==other);
}

/**
 * @brief 重载&运算符，用于矩形与运算
 * @param other - 待运算矩形
 * @return 返回运算结果矩形
 */
template<typename T>
UTRect<T> UTRect<T>::operator&(const UTRect &other) const
{
    if(Intersects(other))
    {
        return UTRect(UTMath::Max(m_tMinX,other.m_tMinX),
                      UTMath::Max(m_tMinY,other.m_tMinY),
                      UTMath::Min(m_tMaxX,other.m_tMaxX),
                      UTMath::Min(m_tMaxY,other.m_tMaxY));
    }
    return UTRect(static_cast<T>(0),static_cast<T>(0),
        static_cast<T>(0),static_cast<T>(0));
}

/**
 * @brief 重载|运算符，用于矩形或运算
 * @param other - 待运算矩形
 * @return 返回运算结果矩形
 */
template<typename T>
UTRect<T> UTRect<T>::operator|(const UTRect &other) const
{
    UTRect rc=*this;
    rc.Merge(other);
    return rc;
}

/**
 * @brief 重载*运算符，用于矩形与数乘算法,从而实现矩形缩放
 * @param scale 矩形缩放比例
 * @return 返回运算结果
 */
template<typename T>
UTRect<T> UTRect<T>::operator*(const UTVector2<T>& scale) const
{
    UTRect rc=*this;
    Point center=rc.GetCenter();
    auto w = rc.GetWidth()*0.5*scale.GetX(),
         h = rc.GetHeight()*0.5*scale.GetY();
    // Point min = {center.GetX() - w, center.GetY() - h};
    
    rc.m_tMinX=center.GetX()-w;
    rc.m_tMinY=center.GetY()-h;
    rc.m_tMaxX=center.GetX()+w;
    rc.m_tMaxY=center.GetY()+h;
    rc.Normalize();
    return rc;
}

/**
 * @brief 检测q点是否与p1、p2围成的矩形相交
 * @param p1   矩形点1
 * @param p2   矩形点2
 * @param q    指定点
 * @return true     如果相交
 * @return false    如果不相交
 */
template<typename T>
bool UTRect<T>::Intersects(const Point& p1,const Point& p2,const Point& q)
{
    if (((q.GetX()>=(p1.GetX()<p2.GetX()?p1.GetX():p2.GetX()))&&
         (q.GetX()<=(p1.GetX()>p2.GetX()?p1.GetX():p2.GetX())))&&
        ((q.GetY()>=(p1.GetY()<p2.GetY()?p1.GetY():p2.GetY()))&&
         (q.GetY()<=(p1.GetY()>p2.GetY()?p1.GetY():p2.GetY()))))
    {
        return true;
    }
    return false;
}

/**
 * @brief 检测由四个点围成的矩形是否相交
 * @param p1   矩形p的点1
 * @param p2   矩形p的点2
 * @param q1   矩形q的点1
 * @param q2   矩形q的点2
 * @return true - 如果相交
 * @return false - 如果不相交
 */
template<typename T>
bool UTRect<T>::Intersects(const Point& p1, const Point& p2,
    const Point& q1, const Point& q2)
{
    T pMin=UTMath::Min(p1.GetX(),p2.GetX());
    T qMax=UTMath::Max(q1.GetX(),q2.GetX());
    if(pMin>qMax)
    {
        return false;
    }
    T qMin=UTMath::Min(q1.GetX(),q2.GetX());
    T pMax=UTMath::Max(p1.GetX(),p2.GetX());
    if(pMax<qMin)
    {
        return false;
    }
    pMin=UTMath::Min(p1.m_dY,p2.m_dY);
    qMax=UTMath::Max(q1.m_dY,q2.m_dY);
    if(pMin>qMax)
    {
        return false;
    }
    pMax=UTMath::Max(p1.m_dY,p2.m_dY);
    qMin=UTMath::Min(q1.m_dY,q2.m_dY);
    if(pMax<qMin)
    {
        return false;
    }
    return true;
}

using IUTRECT=UTRect<int32_t>;
using FUTRECT=UTRect<float>;
using DUTRECT=UTRect<double>;
using MIUTRECT=IUTRECT;
using MFUTRECT=FUTRECT;
using MDUTRECT=DUTRECT;

END_SPACE_LANGE_VDR

#endif // !UT_RECT_H